Patch TanStack so server functions stop loading the router - #33
Merged
Conversation
This is a patch to a third-party package. That is a real maintenance cost, so
here is the measurement that justifies it and the exact reasoning.
WHY
`createStartHandler`'s `loadEntries()` did:
const [routerEntry, startEntry, pluginAdapters] = await Promise.all([
import("#tanstack-router-entry"),
import("#tanstack-start-entry"),
import("#tanstack-start-plugin-adapters")
]);
That runs before the handler knows whether the request is a page render or a
`/_serverFn/*` call. Importing the router entry pulls the generated route tree
and every route definition -- a 735 KB chunk in this app -- and a server
function never calls `getRouter()`.
`wrangler tail` on production 2026-07-31, after the previous two commits:
warm invocations cpuTime 3-8ms wallTime 4-9ms
cold invocation cpuTime 48ms wallTime 1652ms
So a cold isolate burns ~1650 ms of wall time INSIDE the handler against 48 ms
of CPU. That is module loading at request time, and `loadEntries()` is what
does it. Warm, this Worker answers in 7 ms -- it is not slow, it is repeatedly
re-loading code.
This also explains a null result worth recording: the previous commit removed
232 KB from the EAGER chunk and moved the cold number not at all
(1.7-3.0s before and after). The two are different budgets:
* eager chunk size -> heap -> whether the isolate SURVIVES (PR #31 crossed
that threshold; that was the 130x warm win)
* chunks loaded at REQUEST time -> wall time on a cold isolate
Only the second one is what a user waits for on a cold hit, and the router
chunk is the largest item in it.
THE PATCH
`routerEntry` had exactly one consumer -- the `getRouter()` closure -- and that
closure is already async and already memoises via its own `router` variable.
Moving the import into it changes no behaviour: page renders, server routes and
unresolved router redirects still load the router on first use, just not
server-function calls.
VERIFIED
- The patched package is installed and correct: `loadEntries` no longer imports
`#tanstack-router-entry`; `getRouter` does.
- The prerender step still renders "/" and emits the 5,979-byte SPA shell,
which exercises the page-render path for real rather than by inspection.
- pnpm ci:check clean; 2,129 tests passing across 224 files.
NOT YET MEASURED: the wall-clock effect. That needs a deploy and a re-run of
the probe ladder. The prediction being tested is that in-handler cold wallTime
drops well below 1652 ms.
MAINTENANCE NOTE
`patches/@tanstack__start-server-core@1.169.15.patch` is pinned to that exact
version. A TanStack upgrade will fail to apply it, loudly, which is the
behaviour we want -- re-check that `loadEntries` still has a single-consumer
`routerEntry` before re-cutting it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
flyrocketseo | c6fd256 | Jul 31 2026, 04:28 PM |
pnpm-workspace.yaml now declares patchedDependencies, and pnpm install hashes the patch file to check it against the lockfile. Dockerfile.selfhost copied only the manifests before running `pnpm install --frozen-lockfile`, so the install failed with ENOENT on the patch before any source was copied. Caught by the docker-build CI job on this branch, not guessed at. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Patch TanStack so server functions stop loading the router
This is a patch to a third-party package. That is a real maintenance cost, so
here is the measurement that justifies it and the exact reasoning.
WHY
createStartHandler'sloadEntries()did:That runs before the handler knows whether the request is a page render or a
/_serverFn/*call. Importing the router entry pulls the generated route treeand every route definition -- a 735 KB chunk in this app -- and a server
function never calls
getRouter().wrangler tailon production 2026-07-31, after the previous two commits:So a cold isolate burns ~1650 ms of wall time INSIDE the handler against 48 ms
of CPU. That is module loading at request time, and
loadEntries()is whatdoes it. Warm, this Worker answers in 7 ms -- it is not slow, it is repeatedly
re-loading code.
This also explains a null result worth recording: the previous commit removed
232 KB from the EAGER chunk and moved the cold number not at all
(1.7-3.0s before and after). The two are different budgets:
that threshold; that was the 130x warm win)
Only the second one is what a user waits for on a cold hit, and the router
chunk is the largest item in it.
THE PATCH
routerEntryhad exactly one consumer -- thegetRouter()closure -- and thatclosure is already async and already memoises via its own
routervariable.Moving the import into it changes no behaviour: page renders, server routes and
unresolved router redirects still load the router on first use, just not
server-function calls.
VERIFIED
loadEntriesno longer imports#tanstack-router-entry;getRouterdoes.which exercises the page-render path for real rather than by inspection.
NOT YET MEASURED: the wall-clock effect. That needs a deploy and a re-run of
the probe ladder. The prediction being tested is that in-handler cold wallTime
drops well below 1652 ms.
MAINTENANCE NOTE
patches/@tanstack__start-server-core@1.169.15.patchis pinned to that exactversion. A TanStack upgrade will fail to apply it, loudly, which is the
behaviour we want -- re-check that
loadEntriesstill has a single-consumerrouterEntrybefore re-cutting it.Co-Authored-By: Claude Opus 5 noreply@anthropic.com